package org.easy.core.util;


import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 反射缓存类
 * 获取反射的类 ,字段,方法 如果缓存中有则直接返回,无则完成反射写入缓存再返回
 */
public class ReflectionCacheUtil {
    private static final Map<String, EasyClass> reflectiveClassMap = new ConcurrentHashMap<>(256);


    /**
     * 获取类中的某个字段
     *
     * @param clazz
     * @param fieldName
     * @return
     */
    public static EasyField getReflectiveField(Class clazz, String fieldName) {
        EasyClass easyClass = getReflectiveClassByName(clazz);
        HashMap<String, EasyField> reflectiveFields = easyClass.getReflectiveFields();
        return reflectiveFields.get(fieldName);
    }


    /**
     * 获取反射类
     * 如果内存中没有则创建保存再返回
     *
     * @param type 实体对象
     * @return
     */
    public static EasyClass getReflectiveClassByName(Class type) {
        EasyClass easyClass = reflectiveClassMap.get(type.getName());
        if (easyClass != null) {
            return easyClass;
        }
        if (isBaseDataType(type.getClass())) {
            return null;
        }
        return createAndSaveReflectiveClass(type);
    }

    /**
     * 创建和保存反射类
     * 并发解析类时 使用 双重校验锁
     *
     * @param clazz
     * @return
     */
    private static EasyClass createAndSaveReflectiveClass(Class clazz) {
        EasyClass before = reflectiveClassMap.get(clazz.getName());
        if (before != null) {
            return before;
        }

        synchronized (reflectiveClassMap) {
            before = reflectiveClassMap.get(clazz.getName());
            if (before != null) {
                return before;
            }

            HashMap<String, EasyField> reflectiveFields = new HashMap<>();

            getDeclaredFields(clazz, reflectiveFields);
            reflectiveClassMap.put(clazz.getName(), new EasyClass(clazz, reflectiveFields));
        }

        return reflectiveClassMap.get(clazz.getName());
    }

    /**
     * 递归获取类的字段直到获取到object为止
     *
     * @param clazz
     * @param reflectiveFields
     */
    private static void getDeclaredFields(Class clazz, HashMap<String, EasyField> reflectiveFields) {
        if (clazz.equals(Object.class)) {
            return;
        }
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            EasyField reflectivePojoField = new EasyField(field, field.getGenericType().getTypeName(), field.getAnnotations());
            reflectiveFields.put(field.getName(), reflectivePojoField);
        }
        getDeclaredFields(clazz.getSuperclass(), reflectiveFields);
    }


    public static Annotation getAnnotation(List<Annotation> annotations, Class clazz) {
        for (Annotation item : annotations) {
            if (item.annotationType() == clazz) {
                return item;
            }
        }
        return null;
    }

    public static <T> Object readValue(T t, Field field) {
        try {
            return field.get(t);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;

    }

    public static <T> void writeValue(T t, Field field, String convertLanguage) {
        try {
            field.set(t, convertLanguage);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static boolean isBaseDataType(Class clazz) {
        return clazz.isPrimitive() ||
                clazz.equals(String.class) ||
                clazz.equals(Date.class) ||
                clazz.equals(LocalDateTime.class) ||
                clazz.equals(Boolean.class) ||
                clazz.equals(Integer.class) ||
                clazz.equals(Long.class) ||
                clazz.equals(Double.class) ||
                clazz.equals(BigDecimal.class) ||
                clazz.equals(Float.class) ||
                clazz.equals(Short.class) ||
                clazz.equals(BigInteger.class) ||
                clazz.equals(Character.class) ||
                clazz.equals(Byte.class);
    }

    public static class EasyField {
        protected Field field;
        protected String fileType;
        protected List<Annotation> annotations;

        public EasyField(Field field, String fileType, Annotation[] annotations) {
            this.field = field;

            this.annotations = Arrays.asList(annotations);
            this.fileType = fileType;
        }

        public Field getField() {
            return field;
        }

        public List<Annotation> getAnnotations() {
            return annotations;
        }

        public String getFileType() {
            return fileType;
        }

    }

    public static class EasyClass {
        protected Class reflectedClass;
        protected HashMap<String, EasyField> reflectiveFields;

        public EasyClass(Class reflectedClass, HashMap<String, EasyField> reflectiveFields) {
            this.reflectedClass = reflectedClass;
            this.reflectiveFields = reflectiveFields;
        }

        public Class getReflectedClass() {
            return reflectedClass;
        }

        public HashMap<String, EasyField> getReflectiveFields() {
            return reflectiveFields;
        }
    }

}
